﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;

namespace WebSelfHost.Utility.HttpApi
{
    /// <summary>
    /// Http请求处理类
    /// </summary>
    public class HttpProcessor
    {
        private TcpClient client;
        private Stream inputStream;
        private StreamWriter outputStream;
        private string http_method;
        private string http_url;
        private string http_protocol_versionstring;
        private Hashtable httpHeaders = new Hashtable();
        private Dictionary<string, List<MethodInfo>> getMethodDict;
        private Dictionary<string, List<MethodInfo>> postMethodDict;


        private static int MAX_POST_SIZE = 10 * 1024 * 1024; // 最大Post请求数据 10MB

        public HttpProcessor(TcpClient client, Dictionary<string, List<MethodInfo>> getMethodDict, Dictionary<string, List<MethodInfo>> postMethodDict)
        {
            this.client = client;
            this.getMethodDict = getMethodDict;
            this.postMethodDict = postMethodDict;
        }

        /// <summary>
        /// 线程处理方法
        /// </summary>
        public void Process()
        {
            //获得输入流
            inputStream = new BufferedStream(client.GetStream());
            //输出流
            outputStream = new StreamWriter(new BufferedStream(client.GetStream()));
            try
            {
                ParseRequest();
                ReadHeaders();
                if (http_method.Equals("GET"))
                {
                    HandleGETRequest();
                }
                else if (http_method.Equals("POST"))
                {
                    HandlePOSTRequest();
                }
                outputStream.Flush();
                inputStream = null; outputStream = null;
                client.Close();
            }
            catch (Exception e)
            {
                WriteFailure();
            }
        }

        private string StreamReadLine(Stream inputStream)
        {
            int next_char;
            string data = "";
            while (true)
            {
                next_char = inputStream.ReadByte();
                if (next_char == '\n') { break; }
                if (next_char == '\r') { continue; }
                if (next_char == -1) { Thread.Sleep(1); continue; };
                data += Convert.ToChar(next_char);
            }
            return data;
        }


        private void ParseRequest()
        {
            string request = StreamReadLine(inputStream);
            string[] tokens = request.Split(' ');
            if (tokens.Length != 3)
            {
                throw new Exception("invalid http request line");
            }
            http_method = tokens[0].ToUpper();
            http_url = tokens[1];
            http_protocol_versionstring = tokens[2];
            //Log.info("starting: " + request);
        }

        private void ReadHeaders()
        {
            //Log.info("readHeaders()");
            String line;
            while ((line = StreamReadLine(inputStream)) != null)
            {
                if (line.Equals(""))
                {
                    //Log.info("got headers");
                    return;
                }

                int separator = line.IndexOf(':');
                if (separator == -1)
                {
                    throw new Exception("invalid http header line: " + line);
                }
                string name = line.Substring(0, separator);
                int pos = separator + 1;
                while ((pos < line.Length) && (line[pos] == ' '))
                {
                    pos++; // strip any spaces
                }

                string value = line.Substring(pos, line.Length - pos);
                //Log.info(String.Format("header: {0}:{1}", name, value));
                httpHeaders[name] = value;
            }
        }

        private void HandleGETRequest()
        {
            handleGETRequest(this);
        }

        private const int BUF_SIZE = 4096;
        private void HandlePOSTRequest()
        {
            int content_len = 0;
            MemoryStream ms = new MemoryStream();
            if (this.httpHeaders.ContainsKey("Content-Length"))
            {
                content_len = Convert.ToInt32(this.httpHeaders["Content-Length"]);
                if (content_len > MAX_POST_SIZE)
                {
                    throw new Exception(String.Format("POST Content-Length({0}) too big for this simple server", content_len));
                }
                byte[] buf = new byte[BUF_SIZE];
                int to_read = content_len;
                while (to_read > 0)
                {
                    //Log.info(string.Format("starting Read, to_read={0}", to_read));
                    int numread = this.inputStream.Read(buf, 0, Math.Min(BUF_SIZE, to_read));
                    //Log.info(string.Format("read finished, numread={0}", numread));
                    if (numread == 0)
                    {
                        if (to_read == 0)
                        {
                            break;
                        }
                        else
                        {
                            throw new Exception("client disconnected during post");
                        }
                    }
                    to_read -= numread;
                    ms.Write(buf, 0, numread);
                }
                ms.Seek(0, SeekOrigin.Begin);
            }
            //Log.info("get post data end");
            handlePOSTRequest(this, new StreamReader(ms));

        }

        private void WriteSuccess()
        {
            outputStream.WriteLine("HTTP/1.0 200 OK");
            outputStream.WriteLine("Content-Type: text/html");
            outputStream.WriteLine("Connection: close");
            outputStream.WriteLine("");
        }

        private void WriteFailure()
        {
            outputStream.WriteLine("HTTP/1.0 404 File not found");
            outputStream.WriteLine("Connection: close");
            outputStream.WriteLine("");
        }

        private void handleGETRequest(HttpProcessor p)
        {
            p.WriteSuccess();
            try
            {
                string methodName = "";
                if (p.http_url.Contains("?"))
                {
                    methodName = p.http_url.Substring(0, p.http_url.IndexOf("?"));
                }
                else
                {
                    methodName = p.http_url.Substring(0, p.http_url.Length);
                }
                if (!this.getMethodDict.ContainsKey(methodName))
                {
                    p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + string.Format("指令为{0}的GET方法不存在", methodName) + "\"");
                    return;
                }

                //获得参数
                Dictionary<string, string> dict = ParseUrl(p.http_url);


                List<MethodInfo> methodList = this.getMethodDict[methodName];

                List<MethodInfo> methodList2 = new List<MethodInfo>();
                List<MethodInfo> list = new List<MethodInfo>();
                //方法参数个数比较
                //判断重载方法参数格式
                foreach (MethodInfo method in methodList)
                {
                    if (method.GetParameters().Length == dict.Count)
                    {
                        methodList2.Add(method);
                    }
                }
                if (methodList2.Count == 0)
                {
                    p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + string.Format("指令为{0}的GET方法的参数个数不正确", methodName) + "\"");
                    return;
                }

                //参数正确，判断参数名称,遍历所有的方法，如果参数正确
                foreach (MethodInfo method in methodList2)
                {
                    bool flag = true;
                    ParameterInfo[] parms = method.GetParameters();
                    foreach (ParameterInfo parm in parms)
                    {
                        if (!dict.ContainsKey(parm.Name))
                        {
                            flag = false;
                        }
                    }
                    if (flag)
                    {
                        list.Add(method);
                    }
                }
                if (list.Count == 0)
                {
                    p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + string.Format("指令为{0}的GET方法的参数名不正确", methodName) + "\"");
                    return;
                }
                methodList2.Clear();
                MethodInfo useMethod = list[0];
                //回收内存
                list.Clear();
                methodList2 = null;
                list = null;
                //填写参数
                ParameterInfo[] parmsArray = useMethod.GetParameters();
                List<object> parmList = new List<object>();
                foreach (ParameterInfo parmInfo in parmsArray)
                {
                    parmList.Add(dict[parmInfo.Name]);
                }
                //创建实例，调用，static方法就不需要了
                object obj = useMethod.Invoke(null, parmList.ToArray());
                p.outputStream.WriteLine(obj);
            }
            catch (Exception ex)
            {
                p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + ex.Message + "\"");
            }
        }

        private void handlePOSTRequest(HttpProcessor p, StreamReader inputData)
        {
            p.WriteSuccess();
            try
            {
                string methodName = "";
                if (p.http_url.Contains("?"))
                {
                    methodName = p.http_url.Substring(0, p.http_url.IndexOf("?"));
                }
                else
                {
                    methodName = p.http_url.Substring(0, p.http_url.Length);
                }

                if (!postMethodDict.ContainsKey(methodName))
                {
                    p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + string.Format("指令为{0}的POST方法不存在", methodName) + "\"");
                    return;
                }
                string data = "?" + inputData.ReadToEnd();
                inputData.Close();
                //获得参数
                Dictionary<string, string> dict = ParseUrl(data);

                List<MethodInfo> methodList = postMethodDict[methodName];

                List<MethodInfo> methodList2 = new List<MethodInfo>();
                List<MethodInfo> list = new List<MethodInfo>();
                //方法参数个数比较
                //判断重载方法参数格式
                foreach (MethodInfo method in methodList)
                {
                    if (method.GetParameters().Length == dict.Count)
                    {
                        methodList2.Add(method);
                    }
                }
                if (methodList2.Count == 0)
                {
                    p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + string.Format("指令为{0}的POST方法的参数个数不正确", methodName) + "\"");
                    return;
                }

                //参数正确，判断参数名称,遍历所有的方法，如果参数正确
                foreach (MethodInfo method in methodList2)
                {
                    bool flag = true;
                    ParameterInfo[] parms = method.GetParameters();
                    foreach (ParameterInfo parm in parms)
                    {
                        if (!dict.ContainsKey(parm.Name))
                        {
                            flag = false;
                        }
                    }
                    if (flag)
                    {
                        list.Add(method);
                    }
                }
                if (list.Count == 0)
                {
                    p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + string.Format("指令为{0}的POST方法的参数名不正确", methodName) + "\"");
                    return;
                }
                methodList2.Clear();
                MethodInfo useMethod = list[0];
                //回收内存
                list.Clear();
                methodList2 = null;
                list = null;
                //填写参数
                ParameterInfo[] parmsArray = useMethod.GetParameters();
                List<object> parmList = new List<object>();
                foreach (ParameterInfo parmInfo in parmsArray)
                {
                    parmList.Add(dict[parmInfo.Name]);
                }
                //创建实例，调用，static方法就不需要了
                object obj = useMethod.Invoke(null, parmList.ToArray());
                p.outputStream.WriteLine(obj);
            }
            catch (Exception ex)
            {
                p.outputStream.WriteLine("\"Ret\":\"-1\",\"Message\":\"" + ex.Message + "\"");
            }
        }

        /// <summary>
        /// 分析 url 字符串中的参数信息
        /// </summary>
        /// <param name="url">输入的 URL</param>
        /// <param name="baseUrl">输出 URL 的基础部分</param>
        /// <param name="nvc">输出分析后得到的 (参数名,参数值) 的集合</param>
        private static Dictionary<string, string> ParseUrl(string url)
        {
            Dictionary<string, string> nvc = new Dictionary<string, string>();
            if (url == null)
                throw new ArgumentNullException("url");
            if (url == "")
                return nvc;
            int questionMarkIndex = url.IndexOf('?');
            if (questionMarkIndex == -1)
            {
                return nvc;
            }
            if (questionMarkIndex == url.Length - 1)
                return nvc;
            string ps = url.Substring(questionMarkIndex + 1);
            // 开始分析参数对  
            string[] keyValueArray = ps.Split('&');
            foreach (string keyValue in keyValueArray)
            {
                string[] kV = keyValue.Split('=');
                nvc.Add(kV[0], kV[1]);
            }
            return nvc;
        }
    }
}
